/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.mybatis.datascope.interceptor

import com.baomidou.mybatisplus.core.toolkit.PluginUtils
import java.sql.Connection
import java.sql.SQLException
import net.sf.jsqlparser.expression.Expression
import net.sf.jsqlparser.schema.Table
import net.sf.jsqlparser.statement.delete.Delete
import net.sf.jsqlparser.statement.select.PlainSelect
import net.sf.jsqlparser.statement.select.Select
import net.sf.jsqlparser.statement.update.Update
import org.apache.ibatis.executor.Executor
import org.apache.ibatis.executor.statement.StatementHandler
import org.apache.ibatis.mapping.BoundSql
import org.apache.ibatis.mapping.MappedStatement
import org.apache.ibatis.mapping.SqlCommandType
import org.apache.ibatis.session.ResultHandler
import org.apache.ibatis.session.RowBounds
import tony.core.utils.asTo
import tony.mybatis.interceptor.BaseMultiTableInnerInterceptor

/**
 * 数据权限拦截器
 * @author tangli
 * @date 2025/09/15 16:36
 */
internal abstract class DataPermissionInterceptor : BaseMultiTableInnerInterceptor() {
    @Throws(SQLException::class)
    override fun beforeQuery(
        executor: Executor?,
        ms: MappedStatement,
        parameter: Any?,
        rowBounds: RowBounds?,
        resultHandler: ResultHandler<*>?,
        boundSql: BoundSql?,
    ) {
        val mpBs = PluginUtils.mpBoundSql(boundSql)
        mpBs.sql(parserSingle(mpBs.sql(), ms.id))
    }

    override fun beforePrepare(
        sh: StatementHandler,
        connection: Connection?,
        transactionTimeout: Int?,
    ) {
        val mpSh = PluginUtils.mpStatementHandler(sh)
        val ms = mpSh.mappedStatement()
        val sct = ms.sqlCommandType
        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            val mpBs = mpSh.mPBoundSql()
            mpBs.sql(parserMulti(mpBs.sql(), ms.id))
        }
    }

    override fun processSelect(
        select: Select,
        index: Int,
        sql: String?,
        obj: Any,
    ) {
        val whereSegment = obj.asTo<String>()
        processSelectBody(select, whereSegment)
        val withItemsList = select.getWithItemsList()
        if (!withItemsList.isNullOrEmpty()) {
            withItemsList.forEach { withItem -> processSelectBody(withItem.select, whereSegment) }
        }
    }

    /**
     * 设置 where 条件
     *
     * @param plainSelect  查询对象
     * @param whereSegment 查询条件片段
     */
    protected fun setWhere(
        plainSelect: PlainSelect,
        whereSegment: String?,
    ) {
        processPlainSelect(plainSelect, whereSegment)
    }

    /**
     * update 语句处理
     */
    override fun processUpdate(
        update: Update,
        index: Int,
        sql: String?,
        obj: Any?,
    ) {
        getUpdateOrDeleteExpression(
            update.table,
            update.where,
            obj.asTo()
        )?.let { update.where = it }
    }

    /**
     * delete 语句处理
     */
    override fun processDelete(
        delete: Delete,
        index: Int,
        sql: String,
        obj: Any?,
    ) {
        getUpdateOrDeleteExpression(
            delete.table,
            delete.where,
            obj.asTo()
        )?.let { delete.where = it }
    }

    protected fun getUpdateOrDeleteExpression(
        table: Table,
        where: Expression?,
        whereSegment: String?,
    ): Expression? =
        andExpression(table, where, whereSegment)
}
